Débloquez la puissance des portails React pour créer des modales et des info-bulles accessibles et visuellement attrayantes, améliorant l'expérience utilisateur et la structure des composants.
Portails React : Maîtriser les Modales et les Info-bulles pour une UX Améliorée
Dans le développement web moderne, la création d'interfaces utilisateur intuitives et engageantes est primordiale. React, une bibliothèque JavaScript populaire pour construire des interfaces utilisateur, fournit divers outils et techniques pour y parvenir. L'un de ces outils puissants est les portails React. Cet article de blog plonge dans le monde des portails React, en se concentrant sur leur application dans la construction de modales et d'info-bulles accessibles et visuellement attrayantes.
Que sont les portails React ?
Les portails React offrent un moyen de rendre les enfants d'un composant dans un nœud DOM qui existe en dehors de la hiérarchie DOM du composant parent. En termes plus simples, cela vous permet de vous libérer de l'arborescence standard des composants React et d'insérer des éléments directement dans une autre partie de la structure HTML. C'est particulièrement utile pour les situations où vous devez contrôler le contexte d'empilement ou positionner des éléments en dehors des limites de leur conteneur parent.
Traditionnellement, les composants React sont rendus comme des enfants de leurs composants parents dans le DOM. Cela peut parfois entraîner des défis de style et de mise en page, en particulier lorsqu'il s'agit d'éléments comme des modales ou des info-bulles qui doivent apparaître au-dessus d'autres contenus ou être positionnés par rapport à la fenêtre d'affichage. Les portails React fournissent une solution en permettant à ces éléments d'être rendus directement dans une autre partie de l'arborescence DOM, contournant ainsi ces limitations.
Pourquoi utiliser les portails React ?
Plusieurs avantages clés font des portails React un outil précieux dans votre arsenal de développement React :
- Amélioration du style et de la mise en page : Les portails vous permettent de positionner des éléments en dehors du conteneur de leur parent, surmontant les problèmes de style causés par
overflow: hidden, les limitations dez-indexou des contraintes de mise en page complexes. Imaginez une modale qui doit couvrir tout l'écran, même si son conteneur parent aoverflow: hidden. Les portails vous permettent de rendre la modale directement dans lebody, contournant cette limitation. - Accessibilité améliorée : Les portails sont cruciaux pour l'accessibilité, en particulier lorsqu'il s'agit de modales. Rendre le contenu de la modale directement dans le
bodyvous permet de gérer facilement le piégeage du focus, en veillant à ce que les utilisateurs utilisant des lecteurs d'écran ou la navigation au clavier restent à l'intérieur de la modale pendant qu'elle est ouverte. C'est essentiel pour offrir une expérience utilisateur fluide et accessible. - Structure de composant plus propre : En rendant le contenu d'une modale ou d'une info-bulle en dehors de l'arborescence principale des composants, vous pouvez garder votre structure de composant plus propre et plus gérable. Cette séparation des préoccupations peut rendre votre code plus facile à lire, à comprendre et à maintenir.
- Éviter les problèmes de contexte d'empilement : Les contextes d'empilement en CSS peuvent être notoirement difficiles à gérer. Les portails vous aident à éviter ces problèmes en vous permettant de rendre des éléments directement à la racine du DOM, garantissant qu'ils sont toujours positionnés correctement par rapport aux autres éléments de la page.
Implémenter des modales avec les portails React
Les modales sont un modèle d'interface utilisateur courant utilisé pour afficher des informations importantes ou demander une saisie à l'utilisateur. Explorons comment créer une modale à l'aide des portails React.
1. Créer la racine du portail
Tout d'abord, vous devez créer un nœud DOM où la modale sera rendue. Cela se fait généralement en ajoutant un élément div avec un ID spécifique à votre fichier HTML (généralement dans le body) :
<div id="modal-root"></div>
2. Créer le composant de la modale
Ensuite, créez un composant React qui représente la modale. Ce composant contiendra le contenu et la logique de la modale.
import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
const Modal = ({ isOpen, onClose, children }) => {
const [mounted, setMounted] = useState(false);
const modalRoot = useRef(document.getElementById('modal-root'));
useEffect(() => {
setMounted(true);
return () => setMounted(false);
}, []);
if (!isOpen) return null;
const modalContent = (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
{children}
<button onClick={onClose}>Close</button>
</div>
</div>
);
return mounted && modalRoot.current
? ReactDOM.createPortal(modalContent, modalRoot.current)
: null;
};
export default Modal;
Explication :
- Prop
isOpen: Détermine si la modale est visible. - Prop
onClose: Une fonction pour fermer la modale. - Prop
children: Le contenu à afficher à l'intérieur de la modale. - Ref
modalRoot: Fait référence au nœud DOM où la modale sera rendue (#modal-root). - Hook
useEffect: S'assure que la modale n'est rendue qu'après que le composant a été monté pour éviter les problèmes liés au fait que la racine du portail n'est pas disponible immédiatement. ReactDOM.createPortal: C'est la clé pour utiliser les portails React. Il prend deux arguments : l'élément React à rendre (modalContent) et le nœud DOM où il doit être rendu (modalRoot.current).- Cliquer sur la superposition : Ferme la modale. Nous utilisons
e.stopPropagation()sur la divmodal-contentpour empêcher les clics à l'intérieur de la modale de la fermer.
3. Utiliser le composant de la modale
Maintenant, vous pouvez utiliser le composant Modal dans votre application :
import React, { useState } from 'react';
import Modal from './Modal';
const App = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);
return (
<div>
<button onClick={openModal}>Open Modal</button>
<Modal isOpen={isModalOpen} onClose={closeModal}>
<h2>Modal Content</h2>
<p>This is the content of the modal.</p>
</Modal>
</div>
);
};
export default App;
Cet exemple montre comment contrôler la visibilité de la modale à l'aide de la prop isOpen et des fonctions openModal et closeModal. Le contenu à l'intérieur des balises <Modal> sera rendu à l'intérieur de la modale.
4. Styliser la modale
Ajoutez des styles CSS pour positionner et styliser la modale. Voici un exemple de base :
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent background */
display: flex;
justify-content: center;
align-items: center;
z-index: 1000; /* Ensure it's on top of other content */
}
.modal-content {
background-color: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
Explication du CSS :
position: fixed: Assure que la modale couvre toute la fenêtre d'affichage, indépendamment du défilement.background-color: rgba(0, 0, 0, 0.5): Crée une superposition semi-transparente derrière la modale.display: flex, justify-content: center, align-items: center: Centre la modale horizontalement et verticalement.z-index: 1000: Assure que la modale est rendue au-dessus de tous les autres éléments de la page.
5. Considérations d'accessibilité pour les modales
L'accessibilité est cruciale lors de l'implémentation de modales. Voici quelques considérations clés :
- Gestion du focus : Lorsque la modale s'ouvre, le focus doit être automatiquement déplacé vers un élément à l'intérieur de la modale (par exemple, le premier champ de saisie ou un bouton de fermeture). Lorsque la modale se ferme, le focus doit revenir à l'élément qui a déclenché l'ouverture de la modale. Ceci est souvent réalisé en utilisant le hook
useRefde React pour stocker l'élément précédemment focalisé. - Navigation au clavier : Assurez-vous que les utilisateurs peuvent naviguer dans la modale à l'aide du clavier (touche Tab). Le focus doit être piégé à l'intérieur de la modale, empêchant les utilisateurs de sortir accidentellement en utilisant Tab. Des bibliothèques comme
react-focus-lockpeuvent aider à cela. - Attributs ARIA : Utilisez les attributs ARIA pour fournir des informations sémantiques sur la modale aux lecteurs d'écran. Par exemple, utilisez
aria-modal="true"sur le conteneur de la modale etaria-labelouaria-labelledbypour fournir une étiquette descriptive pour la modale. - Mécanisme de fermeture : Fournissez plusieurs moyens de fermer la modale, comme un bouton de fermeture, un clic sur la superposition ou l'appui sur la touche Échap.
Exemple de gestion du focus (avec useRef) :
import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
const Modal = ({ isOpen, onClose, children }) => {
const [mounted, setMounted] = useState(false);
const modalRoot = useRef(document.getElementById('modal-root'));
const firstFocusableElement = useRef(null);
const previouslyFocusedElement = useRef(null);
useEffect(() => {
setMounted(true);
if (isOpen) {
previouslyFocusedElement.current = document.activeElement;
if (firstFocusableElement.current) {
firstFocusableElement.current.focus();
}
const handleKeyDown = (event) => {
if (event.key === 'Escape') {
onClose();
}
};
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
if (previouslyFocusedElement.current) {
previouslyFocusedElement.current.focus();
}
};
}
return () => setMounted(false);
}, [isOpen, onClose]);
if (!isOpen) return null;
const modalContent = (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<h2>Modal Content</h2>
<p>This is the content of the modal.</p>
<input type="text" ref={firstFocusableElement} /> <!-- Premier élément focusable -->
<button onClick={onClose}>Close</button>
</div>
</div>
);
return mounted && modalRoot.current
? ReactDOM.createPortal(modalContent, modalRoot.current)
: null;
};
export default Modal;
Explication du code de gestion du focus :
previouslyFocusedElement.current: Stocke l'élément qui avait le focus avant l'ouverture de la modale.firstFocusableElement.current: Fait référence au premier élément focusable *à l'intérieur* de la modale (dans cet exemple, une saisie de texte).- Lorsque la modale s'ouvre (
isOpenest vrai) :- L'élément actuellement focalisé est stocké.
- Le focus est déplacé vers
firstFocusableElement.current. - Un écouteur d'événements est ajouté pour écouter la touche Échap, qui ferme la modale.
- Lorsque la modale se ferme (fonction de nettoyage) :
- L'écouteur d'événements de la touche Échap est supprimé.
- Le focus est retourné à l'élément qui était précédemment focalisé.
Implémenter des info-bulles avec les portails React
Les info-bulles sont de petites fenêtres contextuelles d'information qui apparaissent lorsqu'un utilisateur survole un élément. Les portails React peuvent être utilisés pour créer des info-bulles qui sont positionnées correctement, indépendamment du style ou de la mise en page de l'élément parent.
1. Créer la racine du portail (si ce n'est pas déjà fait)
Si vous n'avez pas encore créé de racine de portail pour les modales, ajoutez un élément div avec un ID spécifique à votre fichier HTML (généralement dans le body) :
<div id="tooltip-root"></div>
2. Créer le composant d'info-bulle
import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
const Tooltip = ({ text, children, position = 'top' }) => {
const [isVisible, setIsVisible] = useState(false);
const [positionStyle, setPositionStyle] = useState({});
const [mounted, setMounted] = useState(false);
const tooltipRoot = useRef(document.getElementById('tooltip-root'));
const tooltipRef = useRef(null);
const triggerRef = useRef(null);
useEffect(() => {
setMounted(true);
return () => setMounted(false);
}, []);
const handleMouseEnter = () => {
setIsVisible(true);
updatePosition();
};
const handleMouseLeave = () => {
setIsVisible(false);
};
const updatePosition = () => {
if (!triggerRef.current || !tooltipRef.current) return;
const triggerRect = triggerRef.current.getBoundingClientRect();
const tooltipRect = tooltipRef.current.getBoundingClientRect();
let top = 0;
let left = 0;
switch (position) {
case 'top':
top = triggerRect.top - tooltipRect.height - 5; // 5px spacing
left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
break;
case 'bottom':
top = triggerRect.bottom + 5;
left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
break;
case 'left':
top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
left = triggerRect.left - tooltipRect.width - 5;
break;
case 'right':
top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
left = triggerRect.right + 5;
break;
default:
break;
}
setPositionStyle({
top: `${top}px`,
left: `${left}px`,
});
};
const tooltipContent = isVisible && (
<div className="tooltip" style={positionStyle} ref={tooltipRef}>
{text}
</div>
);
return (
<span
ref={triggerRef}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{children}
{mounted && tooltipRoot.current ? ReactDOM.createPortal(tooltipContent, tooltipRoot.current) : null}
</span>
);
};
export default Tooltip;
Explication :
- Prop
text: Le texte à afficher dans l'info-bulle. - Prop
children: L'élément qui déclenche l'info-bulle (l'élément que l'utilisateur survole). - Prop
position: La position de l'info-bulle par rapport à l'élément déclencheur ('top', 'bottom', 'left', 'right'). La valeur par défaut est 'top'. - État
isVisible: Contrôle la visibilité de l'info-bulle. - Ref
tooltipRoot: Fait référence au nœud DOM où l'info-bulle sera rendue (#tooltip-root). - Ref
tooltipRef: Fait référence à l'élément de l'info-bulle lui-même, utilisé pour calculer ses dimensions. - Ref
triggerRef: Fait référence à l'élément qui déclenche l'info-bulle (leschildren). handleMouseEnterethandleMouseLeave: Gestionnaires d'événements pour le survol de l'élément déclencheur.updatePosition: Calcule la position correcte de l'info-bulle en fonction de la proppositionet des dimensions des éléments déclencheur et info-bulle. Il utilisegetBoundingClientRect()pour obtenir la position et les dimensions des éléments par rapport à la fenêtre d'affichage.ReactDOM.createPortal: Rend le contenu de l'info-bulle dans letooltipRoot.
3. Utiliser le composant d'info-bulle
import React from 'react';
import Tooltip from './Tooltip';
const App = () => {
return (
<div>
<p>
Hover over this <Tooltip text="This is a tooltip!
With multiple lines."
position="bottom">text</Tooltip> to see a tooltip.
</p>
<button>
Hover <Tooltip text="Button tooltip" position="top">here</Tooltip> for tooltip.
</button>
</div>
);
};
export default App;
Cet exemple montre comment utiliser le composant Tooltip pour ajouter des info-bulles à du texte et à des boutons. Vous pouvez personnaliser le texte et la position de l'info-bulle à l'aide des props text et position.
4. Styliser l'info-bulle
Ajoutez des styles CSS pour positionner et styliser l'info-bulle. Voici un exemple de base :
.tooltip {
position: absolute;
background-color: rgba(0, 0, 0, 0.8); /* Dark background */
color: white;
padding: 5px;
border-radius: 3px;
font-size: 12px;
z-index: 1000; /* Ensure it's on top of other content */
white-space: pre-line; /* Respect line breaks in the text prop */
}
Explication du CSS :
position: absolute: Positionne l'info-bulle par rapport autooltip-root. La fonctionupdatePositiondans le composant React calcule les valeurs précises detopetleftpour positionner l'info-bulle près de l'élément déclencheur.background-color: rgba(0, 0, 0, 0.8): Crée un fond sombre légèrement transparent pour l'info-bulle.white-space: pre-line: C'est important pour préserver les sauts de ligne que vous pouvez inclure dans la proptext. Sans cela, le texte de l'info-bulle apparaîtrait sur une seule ligne.
Considérations globales et meilleures pratiques
Lors du développement d'applications React pour un public mondial, tenez compte de ces meilleures pratiques :
- Internationalisation (i18n) : Utilisez une bibliothèque comme
react-i18nextouFormatJSpour gérer les traductions et la localisation. Cela vous permet d'adapter facilement votre application à différentes langues et régions. Pour les modales et les info-bulles, assurez-vous que le contenu textuel est correctement traduit. - Support droite-à-gauche (RTL) : Pour les langues qui se lisent de droite à gauche (par exemple, l'arabe, l'hébreu), assurez-vous que vos modales et info-bulles s'affichent correctement. Vous devrez peut-être ajuster le positionnement et le style des éléments pour s'adapter aux mises en page RTL. Les propriétés logiques CSS (par exemple,
margin-inline-startau lieu demargin-left) peuvent être utiles. - Sensibilité culturelle : Soyez conscient des différences culturelles lors de la conception de vos modales et info-bulles. Évitez d'utiliser des images ou des symboles qui pourraient être offensants ou inappropriés dans certaines cultures.
- Fuseaux horaires et formats de date : Si vos modales ou info-bulles affichent des dates ou des heures, assurez-vous qu'elles sont formatées en fonction des paramètres régionaux et du fuseau horaire de l'utilisateur. Des bibliothèques comme
moment.js(bien qu'obsolète, toujours largement utilisée) oudate-fnspeuvent aider à cela. - Accessibilité pour diverses capacités : Respectez les directives d'accessibilité (WCAG) pour vous assurer que vos modales et info-bulles sont utilisables par les personnes handicapées. Cela inclut la fourniture de textes alternatifs pour les images, la garantie d'un contraste de couleur suffisant et la prise en charge de la navigation au clavier.
Conclusion
Les portails React sont un outil puissant pour construire des interfaces utilisateur flexibles et accessibles. En comprenant comment les utiliser efficacement, vous pouvez créer des modales et des info-bulles qui améliorent l'expérience utilisateur et la structure et la maintenabilité de vos applications React. N'oubliez pas de donner la priorité à l'accessibilité et aux considérations globales lors du développement pour un public diversifié, en veillant à ce que vos applications soient inclusives et utilisables par tous.